package com.collins.thread.lock;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

public class SpinLock {
    private static AtomicReference<Thread> lock = new AtomicReference<>();
    private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void lock() {
        reentrantLock.lock();
        while (!lock.compareAndSet(null, Thread.currentThread())) {
            // TODO checking period: 1 second ...
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(df.format(new Date(System.currentTimeMillis()))
                    + ": " + " [INFO] " + Thread.currentThread().getName() + " trying to acquire lock...");
        }
    }

    public static void unlock() {
        lock.compareAndSet(Thread.currentThread(), null);
    }

    public static void main(String[] args) {
        // TODO thread 1
        new Thread(() -> {
            System.out.println(df.format(new Date(System.currentTimeMillis()))
                    + ": " + Thread.currentThread().getName() + " has come in and get lock ...");
            // TODO lock
            SpinLock.lock();
            // sleep for 5 seconds
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // TODO unlock
            SpinLock.unlock();
            System.out.println(df.format(new Date(System.currentTimeMillis()))
                    + ": " + Thread.currentThread().getName() + " releases the lock ...");
        }, "thread-1").start();

        // TODO then sleep for 1 second
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // TODO thread 2
        new Thread(() -> {
            System.out.println(df.format(new Date(System.currentTimeMillis()))
                    + ": " + Thread.currentThread().getName() + " has come in and get lock ...");
            // TODO lock
            SpinLock.lock();
            // supposed to spin for lock ...
            // TODO unlock
            SpinLock.unlock();
            System.out.println(df.format(new Date(System.currentTimeMillis()))
                    + ": " + Thread.currentThread().getName() + " releases the lock ...");
        }, "thread-2").start();
    }
}
